import os
import os.path as osp
from pathlib import Path
import multiprocessing as mp
from multiprocessing.pool import Pool
from datetime import datetime
import json
from copy import deepcopy

from termcolor import colored
import requests
import pandas as pd
import numpy as np
from tqdm import tqdm

from .utils import read_json, write_json, read_txt, write_txt, filter_exist_file
from .widget import SinglePoolBar


class ReturnLabels:
    def __init__(self, headers, url, input_dir, output_dir, debug=False, origin_data=None, filter_data=None, repair_batch=None):
        self.headers = headers
        self.url = url
        self.input_dir = Path(input_dir)
        self.output_dir = output_dir
        self.debug = debug
        self.filter_data = filter_data
        self.origin_data = origin_data
        self.repair_batch = repair_batch

        self.number_of_failure = 0
        self.number_of_valid = 0
        self.number_of_objects = 0
        
        self.error_txt_path = osp.join(self.output_dir, "response_error.txt")
        if osp.exists(self.error_txt_path):
            os.remove(self.error_txt_path)
        self.valid_txt_path = osp.join(self.output_dir, "response_valid.txt")
        if osp.exists(self.valid_txt_path):
            os.remove(self.valid_txt_path)

        self.input_path_list = []
        self.filter_path_list = []

        self._obt_path_list()
        self._load_verify_path()

    def _obt_path_list(self):
        if self.input_dir.is_dir():
            self.input_path_list = [path for path in Path(self.input_dir).rglob("*") if path.is_file() and path.suffix == ".json"]
        if self.input_dir.is_file():
            pass
        else:
            pass
        
        if not osp.exists(self.output_dir):
            os.makedirs(self.output_dir, exist_ok=True)
        else:
            pass

        if self.filter_data != None and self.filter_data !="":
            if Path(self.filter_data).is_dir():
                self.filter_path_list = [path for path in Path(self.filter_data).rglob("*") if path.is_file() and path.suffix == ".json"]
            if Path(self.filter_data).is_file():
                pass
            else:
                pass
        else:
            pass
        
        if self.origin_data != None and self.origin_data !="":
            if Path(self.origin_data).is_dir():
                origin_data = [path for path in Path(self.origin_data).rglob("*") if path.is_file() and path.suffix == ".json"]
            if Path(self.origin_data).is_file():
                origin_data = [self.origin_data]
            else:
                pass
            self.origin_data = origin_data
        else:
            pass

    def _load_verify_path(self):
        if self.filter_data != None and self.filter_data != "":
            self.return_path_list = filter_exist_file(self.input_path_list, self.filter_path_list)
        else:
            self.return_path_list = self.input_path_list

    def save_error_info(self, excel_name):
        if osp.exists(self.error_txt_path):
            data = read_txt(self.error_txt_path)
            self.number_of_failure = len(data)
            # api错误信息
            api_data = {
                "帧名": list(),
                "api验证信息": list(),
            }
            for row in data:
                api_data["帧名"].append(row.rstrip().split("----")[0])
                api_data["api验证信息"].append(row.rstrip().split("----")[1])
            
            if all(api_data.values()):
                df = pd.DataFrame(api_data)
                with pd.ExcelWriter(os.path.join(self.output_dir, excel_name)) as writer:
                    df.to_excel(writer, index=False)

    def save_valid_info(self, excel_name):
        if osp.exists(self.valid_txt_path):
            data = read_txt(self.valid_txt_path)
            # api错误信息
            api_data = {
                "帧名": list(),
                "api验证信息": list(),
                "valid": list()
            }
            for row in data:
                api_data["帧名"].append(row.rstrip().split("----")[0])
                api_data["api验证信息"].append(row.rstrip().split("----")[1])
                api_data["valid"].append(int(row.rstrip().split("----")[2]))

            self.number_of_valid = sum(api_data["valid"])
            
            if all(api_data.values()):
                df = pd.DataFrame(api_data)
                with pd.ExcelWriter(os.path.join(self.output_dir, excel_name)) as writer:
                    df.to_excel(writer, index=False)

    def print_static_info(self):
        static_info = f"""
                    ===> Statics:
                        ===> Loading all data from: '{osp.abspath(self.input_dir)}'
                        ===> Loading all data numbers: {len(self.input_path_list)}
                        
                        ===> Loading already uploaded data from: '{osp.abspath(self.filter_data)}'
                        ===> Loading repetition data numbers: {len(self.input_path_list)-len(self.return_path_list)}
                        ===> Number of need upload: {len(self.return_path_list)} 
                        
                        ===> Number of {colored('upload successes', 'light_green')}: {len(self.return_path_list)-self.number_of_failure}
                        ===> Number of {colored('upload failures', 'light_red')}: {self.number_of_failure}
                        
                        ===> Number of valid: {self.number_of_valid}
                        ===> Number of unvalid: {len(self.return_path_list)-self.number_of_failure-self.number_of_valid}
                        
                        ===> Number of {colored('objects', 'light_green')}: {self.number_of_objects}
                        
                        ===> Save all results in: {osp.abspath(self.output_dir)}
                    """
        write_txt(osp.join(self.output_dir, "static_info.txt"), static_info, mode='w')
        print(static_info)

    def config_return_labels(self, results, json_path):
        if results != None:
            resource_names_ist = []
            for i in results["resources"]:
                for j in i:
                    resource_names_ist.append(j["uri"])
            new_json = {
                "project": results["labeled_data"]["metadata"]["label_project"],
                "rule_version": results["labeled_data"]["metadata"]["label_rule_version"],
                "vin": results["collect_metadata"]["vin"],
                "resource_names": resource_names_ist,
                "supplier": "coredata",
                "labeled_data": results["labeled_data"]
            }
            if len(results["labeled_data"]["frames"][0]["groups"]) == 0:
                new_json["valid"] = "False"

            if self.debug:
                status_code = "debug"
                message = "debug"
            else:
                response = requests.post(url=self.url, json=new_json, headers=self.headers, verify=False)
                status_code = response.status_code
                message = response.json()
            
            print("===> response status_code: ", status_code)
            # print("===> response json: ", message)

            if status_code != 200 and status_code !="debug":
                data = f'{results["resources"][0][0]["uri"]}----{message}\n'
                write_txt(self.error_txt_path, data)
            else:
                output_path = str(json_path).replace(str(self.input_dir), str(self.output_dir))
                os.makedirs(osp.dirname(output_path), exist_ok=True)
                write_json(output_path, new_json)
                data = f'{results["resources"][0][0]["uri"]}----{message}----{results["labeled_data"]["frames"][0]["properties"]["valid"]}\n'
                write_txt(self.valid_txt_path, data)

    def config_repair_labels(self, results, json_path, repair_batch):
        # 返修还标
        if repair_batch != None and repair_batch != "":
            new_json = {
                "repair_batch": repair_batch,
                "vin": results["collect_metadata"]["vin"],
                "resource_names": [results["resources"][0][0]['uri']],
                "supplier": "coredata",
                "labeled_data": results["labeled_data"]
            }

            if self.debug:
                status_code = "debug"
                message = "debug"
            else:
                response = requests.post(url=self.url, json=new_json, headers=self.headers, verify=False)
                status_code = response.status_code
                message = response.json()
            
            print("===> response status_code: ", status_code)
            # print("===> response json: ", message)

            if status_code != 200 and status_code !="debug":
                data = f"{results['resources'][0][0]['uri']}----{message}\n"
                write_txt(self.error_txt_path, data)
            else:
                output_path = str(json_path).replace(str(self.input_dir), str(self.output_dir))
                os.makedirs(os.path.dirname(output_path), exist_ok=True)
                write_json(output_path, new_json)
        else:
            pass


## ----------------------------------------------- parking slot start -----------------------------------------------
class ParkingSlotReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None
                ):
        super().__init__(headers, url, input_dir, output_dir, debug, filter_data=filter_data, repair_batch=repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def parking_slot_return(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.parking_slot_result(path)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_slot_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_slot_return_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def parking_slot_return_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.parking_slot_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_slot_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_slot_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def parking_slot_repair(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.parking_slot_result(path)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_slot_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_slot_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def parking_slot_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.parking_slot_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_slot_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_slot_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def parking_slot_result(self, json_path):
        try:
            json_content = read_json(json_path)
            # print("===> storageURL: ", json_content["storageURL"].split(os.sep))
            group_dict = dict()
            for group in json_content["LabelDetail"]["groupObject"]:
                group_dict[group["container"]] = group["itemList"]
            obects_dict = dict()
            for objects in json_content["LabelDetail"]["objects"]:
                obects_dict[objects["featureId"]] = objects
            
            result = {
                "schema_version": "1.0",    # 写死
                "collect_metadata": {
                    "vin": json_content["storageURL"].split("/")[-4],
                    "collected_time": 1679573801,
                },
                "resources": [[{
                    "sensor": "CameraIPM",    # 写死
                    "uri": json_content["originStorageName"]
                }, 
                ]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": [
                        {
                            "properties": {"valid": "1"},
                            "groups": list()
                        }
                    ]
                }
            }
            groups = result["labeled_data"]["frames"][0]["groups"]
            self.number_of_objects += len(group_dict)
            for group, points in group_dict.items():
                material_g, occupy_g, shape_g, function_g, ignore_g = None, None, None, None, None
                for attr in obects_dict[group]["feature"]["attrs"]:
                    if attr["codeName"].strip() == "material":
                        material_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "occupy":
                        occupy_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "shape":
                        shape_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "function":
                        function_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "ignore":
                        ignore_g = attr["attrs"][0]["codeName"].strip()

                parkingslot_points = list()
                
                # 排序点
                sorted_points = deepcopy(points)
                for i, item in enumerate(points):
                    labelname = obects_dict[item]["labelName"]
                    sorted_points[int(labelname[-1])-1] = points[i]
                
                for point in sorted_points:
                    # print(f'===> obects_dict: {obects_dict[point]}')
                    visible_p, shape_p = None, None
                    for attr in obects_dict[point]["feature"]["attrs"]:
                        if attr["codeName"].strip() == "visible":
                            visible_p = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "shape":
                            shape_p = attr["attrs"][0]["codeName"].strip()
                    # if visible_p == "1":
                        # print(f"===> visible_p: {visible_p} shape_p: {shape_p}")

                    if visible_p == "0":
                        shape_p = "-1"
                    parkingslot_points.append(
                        {
                            "x": float(obects_dict[point]["feature"]["points"][0]["x"]),
                            "y": float(obects_dict[point]["feature"]["points"][0]["y"]),
                            "properties": {
                                "visible": visible_p,
                                "shape": shape_p
                            }
                        }
                    )
                # print(f'===> obects_dict: {obects_dict[group]}')
                parkingslot_objects = [
                    {
                        "sensor": "CameraIPM",
                        "type": "boundary", # 写死
                        "geometry": "polygon_2d",   # 写死
                        "properties": {
                            "material": material_g,
                            "occupy": occupy_g,
                            "shape": shape_g,
                            "function": function_g,
                            "ignore": ignore_g,
                        },
                        "points": parkingslot_points
                    }
                ]
                groups.append(
                    {
                        "type": "parking_slot",
                        "objects": parkingslot_objects
                    }
                )

            if len(groups) == 0:
                result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
            
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            data = f'{json_content["originStorageName"]}----{err}\n'
            write_txt(self.error_txt_path, data)
            return None

## ----------------------------------------------- parking slot end -------------------------------------------------


## ----------------------------------------------- freespace3d start -----------------------------------------------
class Freespace3dReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None
                ):
        super().__init__(headers, url, input_dir, output_dir, debug, filter_data=filter_data, repair_batch=repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def freespace3d_return(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.freespace3d_result(path)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freespace3d_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace3d_return_response_valid.xlsx")

        else:
            pass

        self.print_static_info()

    def freespace3d_return_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.freespace3d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freespace3d_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace3d_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def freespace3d_repair(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.freespace3d_result(path)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freepace3d_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace3d_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def freespace3d_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.freespace3d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freepace3d_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace3d_repair_response_valid.xlsx")

        else:
            pass

        self.print_static_info()

    def freespace3d_result(self, json_path):
        try:
            json_content = read_json(json_path)
            result = {
                "schema_version": "1.0",    # 写死
                "collect_metadata": {
                    "vin": json_path.split(os.sep)[-3],
                    "collected_time": osp.basename(json_path).splitext()[0].split("_")[0],
                },
                "resources": [[{
                    "sensor": "LidarFusion",    # 写死
                    "uri": json_content["originStorageName"]
                }, 
                ]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": [
                        {
                            "properties": {"valid": "1"},
                            "groups": [{
                                "type": "freespace",  # 写死
                                "objects": list()
                            }]
                        }
                    ]
                }
            }
            objects = result["labeled_data"]["frames"][0]["groups"][0]["objects"]
            for obj in json_content["LabelDetail"]["objects"]:
                points = []
                for point in obj["feature"]["pointList"]:
                    points.append({
                        "x": point["x"],
                        "y": point["y"],
                        "z": 0              # 统一设置为 0
                    })
                objects.append({
                    "sensor": "LidarFusion",   # 写死
                    "type": "freespace_line",
                    "geometry": "polygon_3d",
                    "properties": {
                        "type": obj["feature"]["attrCodeList"][0],
                    },
                    "points": points
                })
            if len(objects) == 0:
                result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
        
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            data = f'{json_content["originStorageName"]}----{err}\n'
            write_txt(self.error_txt_path, data)
            return None

## ----------------------------------------------- freespace3d end -------------------------------------------------


## ----------------------------------------------- freespace2d start -----------------------------------------------
class Freespace2dReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None
                ):
        super().__init__(headers, url, input_dir, output_dir, debug, filter_data=filter_data, repair_batch=repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def freespace2d_return(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.freespace2d_result(path)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freespace2d_4_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace2d_4_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def freespace2d_return_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.freespace2d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freespace2d_4_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace2d_4_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def freespace2d_repair(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.freespace2d_result(path)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freepace2d_4_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace2d_4_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def freespace2d_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.freespace2d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="freepace2d_4_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="freespace2d_4_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def freespace2d_result(self, json_path):
        try:
            camera_name = osp.splitext(os.path.basename(json_path))[0].split("_")[-1]
            json_content = read_json(json_path)
            result = {
                "schema_version": "1.0",
                "collect_metadata": {
                    "vin": json_path.split(os.sep)[-4],
                    "collected_time": 1679573801,
                },
                "resources": [[{
                    "sensor": camera_name,
                    "uri": json_content["originStorageName"]
                }]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": [
                        {
                            "properties": {"valid": "1"},
                            "groups": [{
                                "type": "freespace",
                                "objects": list()
                            }]
                        }
                    ]
                }
            }
            
            objects = result["labeled_data"]["frames"][0]["groups"][0]["objects"]
            self.number_of_objects += len(json_content["LabelDetail"]["objects"])
            for obj in json_content["LabelDetail"]["objects"]:
                if obj["labelCode"] == "freespace":
                    objects.append({
                            "sensor": camera_name,
                            "type": "segment",
                            "geometry": "line_2d",
                            "properties": {
                                "type": obj["labelCode"],
                                "obstacle": obj["feature"]["attrs"][0]["attrs"][0]["codeName"]
                            },
                            "points": [
                                {"x": float(point["x"]), "y": float(point["y"])}
                                for point in obj["feature"]["points"]
                            ]
                    })
                else:
                    objects.append({
                            "sensor": camera_name,
                            "type": "segment",
                            "geometry": "line_2d",
                            "properties": {
                                "type": obj["labelCode"],
                            },
                            "points": [
                                {"x": float(point["x"]), "y": float(point["y"])}
                                for point in obj["feature"]["points"]
                            ],
                    })
            
            if len(objects) == 0:
                result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
            
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            data = f'{json_content["originStorageName"]}----{err}\n'
            write_txt(self.error_txt_path, data)
            return None

## ----------------------------------------------- freespace2d end -------------------------------------------------


## ----------------------------------------------- M2 lidar3d start -----------------------------------------------
class Lidar3dReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None, 
                 origin_data=None):
        super().__init__(headers, url, input_dir, output_dir, debug, origin_data, filter_data, repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def config_return_labels(self, results, path_list):
        if results != None:
            resource_names_list = []
            for i in results["resources"]:
                for j in i:
                    resource_names_list.append(j["uri"])
                break
            new_json = {
                "project": results["labeled_data"]["metadata"]["label_project"],
                "rule_version": results["labeled_data"]["metadata"]["label_rule_version"],
                "vin": results["collect_metadata"]["vin"][0],
                "resource_names": resource_names_list,
                "supplier": "coredata",
                "labeled_data": results["labeled_data"]
            }
            if len(results["labeled_data"]["frames"]) == 0:
                new_json["valid"] = "False"
            if self.debug:
                status_code = "debug"
                message = "debug"
            else:
                response = requests.post(url=self.url, json=new_json, headers=self.headers, verify=False)
                status_code = response.status_code
                message = response.json()
            
            print("===> response status_code: ", status_code)
            # print("===> response json: ", message)

            if status_code != 200 and status_code !="debug":
                data = f'{results["resources"][0][0]["uri"]}----{message}\n'
                write_txt(self.error_txt_path, data)
            else:
                for i, json_path in enumerate(path_list):
                    output_path = str(json_path).replace(str(self.input_dir), str(self.output_dir))
                    os.makedirs(osp.dirname(output_path), exist_ok=True)
                    write_json(output_path, new_json)
                    data = f'{results["resources"][0][0]["uri"]}----{message}----{results["labeled_data"]["frames"][i]["properties"]["valid"]}\n'
                    write_txt(self.valid_txt_path, data)

    def config_repair_labels(self, results, path_list, repair_batch):
        # 返修还标
        if repair_batch != None and repair_batch != "":
            resource_names_list = []
            for i in results["resources"]:
                for j in i:
                    resource_names_list.append(j["uri"])
                break
            new_json = {
                "repair_batch": repair_batch,
                "vin": results["collect_metadata"]["vin"][0],
                "resource_names": resource_names_list,
                "supplier": "coredata",
                "labeled_data": results["labeled_data"]
            }
            # print("===> self.debug: ", self.debug)
            
            if len(results["labeled_data"]["frames"]) == 0:
                new_json["valid"] = "False"
            if self.debug:
                status_code = "debug"
                message = "debug"
            else:
                response = requests.post(url=self.url, json=new_json, headers=self.headers, verify=False)
                status_code = response.status_code
                message = response.json()
            
            print("===> response status_code: ", status_code)
            # print("===> response json: ", message)

            if status_code != 200 and status_code !="debug":
                data = f"{results['resources'][0][0]['uri']}----{message}\n"
                write_txt(self.error_txt_path, data)
            else:
                for i, json_path in enumerate(path_list):
                    output_path = str(json_path).replace(str(self.input_dir), str(self.output_dir))
                    os.makedirs(osp.dirname(output_path), exist_ok=True)
                    write_json(output_path, new_json)
                    data = f'{results["resources"][0][0]["uri"]}----{message}----{results["labeled_data"]["frames"][i]["properties"]["valid"]}\n'
                    write_txt(self.valid_txt_path, data)
        else:
            pass

    def lidar3d_return(self):
        bar = SinglePoolBar()
        print("===> make path dict...")
        path_dict = dict()
        for i, path in enumerate(self.return_path_list):
            path_dict[Path(path).stem] = path
        # print("===> path_dict: ", path_dict)

        print("===> make data dict...")
        lidar3d_data_list = list()
        for origin_data in self.origin_data:
            for i, data in enumerate(obt_lidar3d_origin_data(origin_data)):
                lidar3d_data_list.append(data)
        
        for i, lidar3d_data in enumerate(lidar3d_data_list):
            # print("===> lidar3d_data: ", lidar3d_data)
            return_path_list = []
            return_uri_rgb_list = []
            for uri_pcd, uri_rgb in lidar3d_data.items():
                if uri_pcd in path_dict:
                    # print("===> uri_pcd: ", uri_pcd)
                    return_path_list.append(path_dict[uri_pcd])
                    return_uri_rgb_list.append(uri_rgb)
            if len(return_path_list) != 0 or len(return_uri_rgb_list) != 0:
                results = self.lidar3d_result(return_path_list, return_uri_rgb_list)
                self.config_return_labels(results, return_path_list)
            bar.set_value(len(lidar3d_data_list), i)
        bar.close()

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="lidar3d_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="lidar3d_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def lidar3d_return_pool(self):
        lidar3d_uri_dict = {}
        for origin_data in self.origin_data:
            lidar3d_uri_dict.update(obt_lidar3d_origin_data(origin_data))
        
        return_name_list = [path.stem for path in self.return_path_list]
        uri_rgb_list = []
        for uri in lidar3d_uri_dict:
            for uri_key, uri_value in uri.items():
                if uri_key in return_name_list:
                    uri_rgb_list.append(uri_value)
        
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.lidar3d_result, zip(self.return_path_list, uri_rgb_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        try:
            self.save_error_info(excel_name="lidar3d_return_response_error.xlsx")
            self.save_valid_info(excel_name="lidar3d_return_response_valid.xlsx")

        except:
            pass

        self.print_static_info()

    def lidar3d_repair(self):
        bar = SinglePoolBar()
        print("===> make path dict...")
        path_dict = dict()
        for i, path in enumerate(self.return_path_list):
            path_dict[Path(path).stem] = path
        # print("===> path_dict: ", path_dict)

        print("===> make data dict...")
        lidar3d_data_list = list()
        for origin_data in self.origin_data:
            for i, data in enumerate(obt_lidar3d_origin_data(origin_data)):
                lidar3d_data_list.append(data)
        
        for i, lidar3d_data in enumerate(lidar3d_data_list):
            # print("===> lidar3d_data: ", lidar3d_data)
            return_path_list = []
            return_uri_rgb_list = []
            for uri_pcd, uri_rgb in lidar3d_data.items():
                if uri_pcd in path_dict:
                    # print("===> uri_pcd: ", uri_pcd)
                    return_path_list.append(path_dict[uri_pcd])
                    return_uri_rgb_list.append(uri_rgb)
            if len(return_path_list) != 0 or len(return_uri_rgb_list) != 0:
                results = self.lidar3d_result(return_path_list, return_uri_rgb_list)
                self.config_repair_labels(results, return_path_list, self.repair_batch)
            bar.set_value(len(lidar3d_data_list), i)
        bar.close()
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="lidar3d_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="lidar3d_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def lidar3d_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.lidar3d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        try:
            self.save_error_info(excel_name="lidar3d_repair_response_error.xlsx")
            self.save_valid_info(excel_name="lidar3d_repair_response_valid.xlsx")
        except:
            pass

        self.print_static_info()

    def lidar3d_result(self, json_path_list, uri_rgb_list):
        # try:
            result = {
                "schema_version": "1.0",    # 写死
                "collect_metadata": {
                    "vin": list(),     #  从点云路径中获取
                    "collected_time": 1111111111,  # 从点云名中获取
                },
                "resources": list(),
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",     # 供应商
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": list()
                }
            }
            frames = result["labeled_data"]["frames"]
            resources_list = []
            vin_list = []
            for json_path, uri_rgb in zip(json_path_list, uri_rgb_list):
                # print("===> json_path: ", json_path)
                json_content = read_json(json_path)
                vin_list.append(json_content["storageURL"].split("/")[-4])
                # collected_time = str(Path(json_path).name).split("_")[0]
                sensor_pcd = str(Path(json_path).name).split("_")[-1]
                uri_pcd = json_content["originStorageName"]
                sensor_rgb = json_content["LabelDetail"]["mappingObject"][0]["cameraName"]
                uri_rgb = uri_rgb
                resources_list.append([{
                    "sensor": sensor_pcd,  # 点云雷达编号
                    "uri": uri_pcd,     # pcd 链接，只要 pcd 文件名，xxx.pcd
                },{
                    "sensor": sensor_rgb,  # 摄像头编号
                    "uri": uri_rgb,     # 图片链接，只要 image 文件名, xxx.jpeg
                }])
                
                feature_id_set = list()
                frame = {
                    "properties": {
                        "valid": "1",   #  该图片是否有效，既是否可标注，写死为 1
                    },
                    "groups": list()
                        }
                groups = frame["groups"]
                self.number_of_objects += len(json_content["LabelDetail"]["objects"])
                for obj in json_content["LabelDetail"]["objects"]:
                    # if float(obj["feature"]["rotation"]["z"]) < -np.pi:
                        # print("===> rotation: \n", obj["feature"]["rotation"], json_path)
                    category = obj["labelCode"]
                    
                    if not obj["featureId"] in feature_id_set:
                        feature_id_set.append(obj["featureId"])
                    track_id = feature_id_set.index(obj["featureId"])+1
                    
                    sub_category, door_opened, people_driving, group_cyclist, truncated, occluded, have_real_edge = None, None, None, None, None, None, None
                    for attr in obj["feature"]["attrs"]:
                        if attr["codeName"].strip() == "sub_category":
                            sub_category = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "door_opened":
                            door_opened = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "people_driving":
                            people_driving = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "group":
                            group_cyclist = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "truncated":
                            truncated = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "occluded":
                            occluded = attr["attrs"][0]["codeName"].strip()
                        if attr["codeName"].strip() == "have_real_edge":
                            have_real_edge = attr["attrs"][0]["codeName"].strip()
                    
                    if 'y' in obj['feature']['frontFaceDirection']:
                        w, h, d = float(obj['feature']['height']), float(obj['feature']['width']), float(obj['feature']['depth'])
                    else:
                        w, h, d = float(obj['feature']['width']), float(obj['feature']['height']), float(obj['feature']['depth'])
                    cube = {
                        "position": {
                            "x": obj['feature']['center']['x'],
                            "y": obj['feature']['center']['y'],
                            "z": obj['feature']['center']['z'],
                        },
                        "rotation": {
                            "pitch": 0,
                            "yaw": obj['feature']['courseAngle'],
                            "roll": 0
                        },
                        "dimension": {
                            "width": h,
                            "height": d,
                            "length": w
                        }
                    }
                    if -np.pi> cube["rotation"]["yaw"] > np.pi:
                        data = f'{osp.basename(json_path)}----yaw 值不在 -pi~pi 之间\n'
                        write_txt(self.error_txt_path, data)
                        print("===> cube.rotation.yaw: ", json_path, cube["rotation"]["yaw"])

                    group = {
                        "type": "detected_object",  # 写死
                        "properties": {
                            "category": category,    #  vehicle 机动车、pedestrian 行人、cyclist 骑行人、static_object 静态物体、others 其他物体
                            "sub_category": sub_category,
                            "door_opened": door_opened,         #  是否开着门: 0 没有、1 开着
                            "people_driving": people_driving,   # 有人在骑行: 0 没有、1 有
                            "group": group_cyclist,             # 是否成组: 0 不成组、1 成组
                            "track_id": track_id,               #  物体 ID，用于物体追踪
                        },
                        "objects": [{
                            "sensor": "FrontLidar01",    # 传感器编号 写死
                            "type": "FrontLidar01_bbox",    # 写死
                            "geometry": "cube",     # 写死 
                            "properties": {
                                "truncated": truncated,   #  0 完全可见、1 小部分截断、2 大部分截断、3 完全截断
                                "occluded": occluded,     #  0 完全可见、1 小部分遮挡、2 大部分遮挡、3 完全遮挡、4 点云可见，图像上几乎不可见
                                "have_real_edge": have_real_edge,  #  有真实长度的边, 1 有，0 没有
                            },
                            # "extra_properties": {
                            #     "score": float,
                            # },
                            
                            "cube": cube
                        }]
                    }
                    if category == "vehicle":
                        group["properties"].pop("people_driving")
                        group["properties"].pop("group")

                    if category == "pedestrian":
                        group["properties"]["sub_category"] = "pedestrian"
                        group["properties"].pop("door_opened")
                        group["properties"].pop("people_driving")
                        group["properties"].pop("group")

                    if category == "cyclist":
                        group["properties"].pop("door_opened")

                    if category in ["static_object", "others"]:
                        group["properties"].pop("door_opened")
                        group["properties"].pop("people_driving")
                        group["properties"].pop("group")

                    groups.append(group)

                if len(groups) == 0:
                    frame["properties"]["valid"] = "0"
                frames.append(frame)
            # print("===> vin_list: ", vin_list)
            result["collect_metadata"]["vin"] = vin_list
            result["resources"] = resources_list
            return result

        # except Exception as err:
        #     print("===> json_content error: ", json_path_list)
        #     print("===> error info: ", err)
        #     return None

def obt_lidar3d_origin_data(data_path):
    # print("===> data_path: ", data_path)
    data = read_json(data_path)
    data_list = []
    for datasets in data["dataset"]:
        uri_dict = dict()
        for frames in datasets["collected_frames"]:
            uri_key, uri_value = None, None
            for frame in frames["resources"]:
                # print("===> frame: ", frame)
                if frame["uri"].split("/")[-1].split("?")[0].split(".")[-1] == "pcd":
                    uri_key = Path(frame["uri"].split("/")[-1].split("?")[0]).stem
                else:
                    uri_value = frame["uri"].split("/")[-1].split("?")[0]

            uri_dict[uri_key] = uri_value
        data_list.append(uri_dict)
    # print("===> len(data_list): ", len(data_list[1]))
    return data_list

## ----------------------------------------------- M2 lidar3d end -------------------------------------------------


## ----------------------------------------------- road marker arrow start -----------------------------------------------
class RoadMarkerArrowReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None
                ):
        super().__init__(headers, url, input_dir, output_dir, debug, filter_data=filter_data, repair_batch=repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def road_marker_arrow_return(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.road_marker_arrow_result(path)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="road_marker_arrow_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="road_marker_arrow_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def road_marker_arrow_return_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.road_marker_arrow_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="road_marker_arrow_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="road_marker_arrow_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def road_marker_arrow_repair(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.road_marker_arrow_result(path)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="road_marker_arrow_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="road_marker_arrow_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def road_marker_arrow_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.road_marker_arrow_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="road_marker_arrow_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="road_marker_arrow_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def road_marker_arrow_result(self, json_path):
        name_dict = {"箭头": "arrow", "禁止符号": "forbidden_symbol", "虚线段": "dashed_line"}
        try:
            json_content = read_json(json_path)
            camera_name = osp.splitext(os.path.basename(json_path))[0].split("_")[-1]
            groups = []
            objects_dict = {}
            self.number_of_objects += len(json_content["LabelDetail"]["objects"])
            for obj in json_content["LabelDetail"]["objects"]:
                if obj["labelName"] == "忽略框":
                    object_type = "rotation_bbox"
                    geometry = "box_2d_rotation"
                    points = [{"x": float(pt["x"]), "y": float(pt["y"])} 
                              for pt in obj["feature"]["points"]]
                    properties = {
                        "category": "traffic_marker"    # 写死
                    }
                    group = {
                        "type": "ignore_box",   # 写死
                        "objects": [{
                            "sensor": camera_name,
                            "type": object_type,
                            "geometry": geometry,
                            "properties": properties,
                            "points": points,
                        }]
                    }
                    groups.append(group)
                else:
                    objects_dict[obj["featureId"]] = obj
            
            for gobj in json_content["LabelDetail"]["groupObject"]:
                objects_unignore = []
                for item in gobj["itemList"]:
                    obj = objects_dict[item]
                    if obj["labelName"] in name_dict:
                        object_type = "rotation_bbox"
                        geometry = "box_2d_rotation"
                        points = [{"x": float(pt["x"]), "y": float(pt["y"])} 
                                for pt in obj["feature"]["points"]]
                        clarity, completeness, bending, standard = None, None, None, None
                        if len(obj["feature"]["attrs"]) != 0:
                            for attrs in obj["feature"]["attrs"]:
                                if attrs["codeName"].strip() == "clarity":
                                    clarity = attrs["attrs"][0]["codeName"].strip()
                                if attrs["codeName"].strip() == "completeness":
                                    completeness = attrs["attrs"][0]["codeName"].strip()
                                if attrs["codeName"].strip() == "bending":
                                    bending = attrs["attrs"][0]["codeName"].strip()
                                if attrs["codeName"].strip() == "standard":
                                    standard = attrs["attrs"][0]["codeName"].strip()
                        properties = {
                            "type": name_dict[obj["labelName"]],
                            "clarity": clarity,
                            "completeness": completeness,
                            "bending": bending,
                            "standard": standard,
                        }
                        if obj["labelName"] in ["禁止符号", "虚线段"]:
                            properties.pop("standard")
                        objects_unignore.append({
                            "sensor": camera_name,
                            "type": object_type,
                            "geometry": geometry,
                            "properties": properties,
                            "points": points,
                        })
                    else:
                        object_type = "key_point"
                        geometry = "point"

                        point_type, visible = None, None
                        if len(obj["feature"]["attrs"]) != 0:
                            for attrs in obj["feature"]["attrs"]:
                                if attrs["codeName"].strip() == "type":
                                    point_type = attrs["attrs"][0]["codeName"].strip()
                                if attrs["codeName"].strip() == "visible":
                                    visible = attrs["attrs"][0]["codeName"].strip()
                        properties = {
                            "type": point_type,
                            "visible": visible,
                        }
                        points = [{"x": float(pt["x"]), "y": float(pt["y"]), "properties": properties} 
                                for pt in obj["feature"]["points"]]
                        objects_unignore.append({
                            "sensor": camera_name,
                            "type": object_type,
                            "geometry": geometry,
                            "points": points,
                        })

                if len(objects_unignore) != 0:
                    group = {
                        "type": "marker_arrow_line",    # 写死
                        "objects": objects_unignore
                    }
                    groups.append(group)
            
            result = {
                "schema_version": "1.0",
                "collect_metadata": {
                    "vin": json_content["storageURL"].split("/")[-4],
                    "collected_time": 1679573801,
                },
                "resources": [[{
                    "sensor": camera_name,
                    "uri": json_content["originStorageName"]
                }]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": [
                        {
                            "properties": {"valid": "1"},
                            "groups": groups,
                        }
                    ]
                }
            }

            if len(groups) == 0:
                result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
            
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            data = f'{json_content["originStorageName"]}----{err}\n'
            write_txt(self.error_txt_path, data)
            return None

## ----------------------------------------------- road marker arrow end -------------------------------------------------


## ----------------------------------------------- parking text recognition start -----------------------------------------------
# TODO
class ParkingTextReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None
                ):
        super().__init__(headers, url, input_dir, output_dir, debug, filter_data=filter_data, repair_batch=repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def parking_text_return(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.parking_text_result(path)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_text_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_text_return_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def parking_text_return_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.parking_text_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_text_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_text_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def parking_text_repair(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.parking_text_result(path)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_text_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_text_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def parking_text_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.parking_text_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="parking_text_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="parking_text_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def parking_text_result(self, json_path):
        try:
            json_content = read_json(json_path)
            # print("===> storageURL: ", json_content["storageURL"].split(os.sep))
            group_dict = dict()
            for group in json_content["LabelDetail"]["groupObject"]:
                group_dict[group["container"]] = group["itemList"]
            obects_dict = dict()
            for objects in json_content["LabelDetail"]["objects"]:
                obects_dict[objects["featureId"]] = objects
            
            result = {
                "schema_version": "1.0",    # 写死
                "collect_metadata": {
                    "vin": json_content["storageURL"].split("/")[-4],
                    "collected_time": 1679573801,
                },
                "resources": [[{
                    "sensor": "CameraIPM",    # 写死
                    "uri": json_content["originStorageName"]
                }, 
                ]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": list(),
                }
            }

            frame = {
                        "properties": {
                            "valid": "1",
                            "recognizable": str,    # 识别度 如果 valid 为 0 则不添加该属性
                            "incomplete": str,      # 残缺程度 如果 valid 为 0 则不添加该属性
                            "incomplete_type": str, # 残缺类型 只有 incomplete 为 2 时 该属性有效
                            "content": str,         # 文本内容 如果 incomplete 为 2 或 valid 为 0 不添加该属性
                        },
                    }


            # if len(groups) == 0:
            #     result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
            
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            return None

## ----------------------------------------------- parking text recognition end -------------------------------------------------


## ----------------------------------------------- fisheye mono 3d start -----------------------------------------------
class FisheyeMonoReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None, 
                 origin_data=None):
        super().__init__(headers, url, input_dir, output_dir, debug, origin_data, filter_data, repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def fisheye_mono_return(self):
        name_pair = {}
        for idx, origin_data in enumerate(self.origin_data):
            print(f"===> load origin data: {idx+1}/{len(self.origin_data)}")
            name_pair.update(obt_fisheye_mono_origin_data(origin_data))
        # print("===> name_pair: ", name_pair)
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            camera_uri = name_pair[str(Path(path).stem)]
            results = self.fisheye_mono_result(path, camera_uri)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="fisheye_mono_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="fisheye_mono_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def fisheye_mono_return_pool(self):
        name_pair = []
        for idx, origin_data in enumerate(self.origin_data):
            print(f"===> load origin data: {idx+1}/{len(self.origin_data)}")
            name_pair.extend(obt_fisheye_mono_origin_data(origin_data))
        
        camera_uri_list = []
        print(f"===> Obtent camera uri...")
        for path in tqdm(self.return_path_list):
            camera_uri_list.append(name_pair[Path(path).stem])

        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.fisheye_mono_result, zip(self.return_path_list, camera_uri_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="fisheye_mono_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="fisheye_mono_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def fisheye_mono_repair(self):
        name_pair = {}
        for idx, origin_data in enumerate(self.origin_data):
            print(f"===> load origin data: {idx+1}/{len(self.origin_data)}")
            name_pair.update(obt_fisheye_mono_origin_data(origin_data))
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            camera_uri = name_pair[str(Path(path).stem)]
            results = self.fisheye_mono_result(path, camera_uri)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="fisheye_mono_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="fisheye_mono_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def fisheye_mono_repair_pool(self):
        name_pair = []
        for idx, origin_data in enumerate(self.origin_data):
            print(f"===> load origin data: {idx+1}/{len(self.origin_data)}")
            name_pair.extend(obt_fisheye_mono_origin_data(origin_data))
        
        camera_uri_list = []
        print(f"===> Obtent camera uri...")
        for path in tqdm(self.return_path_list):
            camera_uri_list.append(name_pair[Path(path).stem])

        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.fisheye_mono_result, zip(self.return_path_list, camera_uri_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="fisheye_mono_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="fisheye_mono_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def fisheye_mono_result(self, json_path, camera_uri):
        try:
            json_content = read_json(json_path)
            sensor_name = osp.splitext(os.path.basename(json_path))[0].split("_")[-1]
            objects_dict = {}
            self.number_of_objects += len(json_content["LabelDetail"]["objects"])
            for obj in json_content["LabelDetail"]["objects"]:
                feature_id = obj["featureId"]
                if feature_id in objects_dict:
                    objects_dict[feature_id].append(obj)
                else:
                    objects_dict[feature_id] = [obj]
            
            groups = []
            for key, obj_list in objects_dict.items():
                objects_list = []
                category, sub_category, door_opened, people_driving, group_cyclist = None, None, None, None, None
                for obj in obj_list:
                    if obj["type"] == "POINTCLOUD":
                        category = obj["labelCode"]

                        if 'y' in obj['feature']['frontFaceDirection']:
                            w, h, d = float(obj['feature']['height']), float(obj['feature']['width']), float(obj['feature']['depth'])
                        else:
                            w, h, d = float(obj['feature']['width']), float(obj['feature']['height']), float(obj['feature']['depth'])
                        cube = {
                            "position": {
                                "x": obj['feature']['center']['x'],
                                "y": obj['feature']['center']['y'],
                                "z": obj['feature']['center']['z'],
                            },
                            "rotation": {
                                "pitch": 0,
                                "yaw": obj['feature']['courseAngle'],
                                "roll": 0
                            },
                            "dimension": {
                                "width": h,
                                "height": d,
                                "length": w
                            }
                        }
                        if -np.pi> cube["rotation"]["yaw"] > np.pi:
                            print("===> cube.rotation.yaw: ", json_path, cube["rotation"]["yaw"])

                        objects_list.append({
                            "sensor": sensor_name,
                            "type": sensor_name+"_bbox",
                            "geometry": "cube",
                            "cube": cube
                        })
                    else:
                        category = obj["labelCode"]
                        camera_name = obj["cameraName"]
                        truncated, occluded = None, None
                        if len(obj["feature"]["attrs"]) != 0:
                            for attr in obj["feature"]["attrs"]:
                                if attr["codeName"].strip() == "sub_category":
                                    sub_category = attr["attrs"][0]["codeName"].strip()
                                if attr["codeName"].strip() == "door_opened":
                                    door_opened = attr["attrs"][0]["codeName"].strip()
                                if attr["codeName"].strip() == "people_driving":
                                    people_driving = attr["attrs"][0]["codeName"].strip()
                                if attr["codeName"].strip() == "group":
                                    group_cyclist = attr["attrs"][0]["codeName"].strip()
                                if attr["codeName"].strip() == "truncated":
                                    truncated = attr["attrs"][0]["codeName"].strip()
                                if attr["codeName"].strip() == "occluded":
                                    occluded = attr["attrs"][0]["codeName"].strip()
                        x_list = []
                        y_list = []
                        for point in obj["feature"]["points"]:
                            x_list.append(float(point["x"]))
                            y_list.append(float(point["y"]))
                        objects_list.append({
                            "sensor": obj["cameraName"],
                            "type": obj["cameraName"]+"_bbox",
                            "geometry": "box_2d",
                            "properties": {
                                "truncated": truncated,
                                "occluded": occluded,
                            },
                            "points": [{"x": min(x_list), "y": min(y_list)}, {"x": max(x_list), "y": max(y_list)}]
                        })
                group = {
                    "type": "detected_object",
                    "properties": {
                        "category": category,
                        "sub_category": sub_category,
                        "door_opened": door_opened,
                        "people_driving": people_driving,   # 有人在骑行: 0 没有、1 有
                        "group": group_cyclist,             # 是否成组: 0 不成组、1 成组
                    },
                    "objects": objects_list
                }
                if category == "vehicle":
                    group["properties"].pop("people_driving")
                    group["properties"].pop("group")

                if category == "pedestrian":
                    group["properties"]["sub_category"] = "pedestrian"
                    group["properties"].pop("door_opened")
                    group["properties"].pop("people_driving")
                    group["properties"].pop("group")

                if category == "cyclist":
                    group["properties"].pop("door_opened")

                if category in ["static_object", "others"]:
                    group["properties"].pop("door_opened")
                    group["properties"].pop("people_driving")
                    group["properties"].pop("group")
                groups.append(group)

            result = {
                "schema_version": "1.0",
                "collect_metadata": {
                    "vin": json_content["storageURL"].split("/")[-4],
                    "collected_time": 1679573801,
                },
                "resources": [[
                    {
                        "sensor": sensor_name,
                        "uri": json_content["originStorageName"]
                    },
                    {
                        "sensor": camera_name,
                        "uri": camera_uri
                    },
                ]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": [
                        {
                            "properties": {"valid": "1"},
                            "groups": groups,
                        }
                    ]
                }
            }

            if len(groups) == 0:
                result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
            
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            data = f'{json_content["originStorageName"]}----{err}\n'
            write_txt(self.error_txt_path, data)
            return None

def obt_fisheye_mono_origin_data(data_path):
    data = read_json(data_path)
    uri_dict = dict()
    for datasets in tqdm(data["dataset"]):
        for frames in datasets["collected_frames"]:
            uri_key, uri_value = None, None
            for frame in frames["resources"]:
                # print("===> frame: ", frame)
                if frame["uri"].split("/")[-1].split("?")[0].split(".")[-1] == "pcd":
                    uri_key = Path(frame["uri"].split("/")[-1].split("?")[0]).stem
                else:
                    uri_value = frame["uri"].split("/")[-1].split("?")[0]

            uri_dict[uri_key] = uri_value
    return uri_dict

## ----------------------------------------------- fisheye mono 3d end -------------------------------------------------


## ----------------------------------------------- sign keypoint 2D start -----------------------------------------------
class SignKeypoint2DReturnLabels(ReturnLabels):
    def __init__(self, 
                 headers, 
                 url, 
                 label_project, 
                 label_rule_version, 
                 input_dir, 
                 output_dir, 
                 debug=False, 
                 filter_data=None, 
                 repair_batch=None
                ):
        super().__init__(headers, url, input_dir, output_dir, debug, filter_data=filter_data, repair_batch=repair_batch)
        self.label_project = label_project
        self.label_rule_version = label_rule_version

    def sign_keypoint_2d_return(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.sign_keypoint_2d_result(path)
            self.config_return_labels(results, path)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="sign_keypoint_2d_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="sign_keypoint_2d_return_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def sign_keypoint_2d_return_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.sign_keypoint_2d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_return_labels, zip(results, self.return_path_list))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="sign_keypoint_2d_return_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="sign_keypoint_2d_return_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def sign_keypoint_2d_repair(self):
        bar = SinglePoolBar()
        for i, path in enumerate(self.return_path_list):
            results = self.sign_keypoint_2d_result(path)
            self.config_repair_labels(results, path, self.repair_batch)
            bar.set_value(len(self.return_path_list), i)
        bar.close()
        
        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="sign_keypoint_2d_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="sign_keypoint_2d_repair_response_valid.xlsx")
        else:
            pass
        
        self.print_static_info()

    def sign_keypoint_2d_repair_pool(self):
        with Pool(mp.cpu_count()) as results_pool:
            results = results_pool.starmap(self.sign_keypoint_2d_result, zip(self.return_path_list))
        with Pool(mp.cpu_count()) as upload_pool:
            upload_pool.starmap(self.config_repair_labels, zip(results, self.return_path_list, [self.repair_batch]*len(self.return_path_list)))

        if osp.exists(self.error_txt_path):
            self.save_error_info(excel_name="sign_keypoint_2d_repair_response_error.xlsx")
        if osp.exists(self.valid_txt_path):
            self.save_valid_info(excel_name="sign_keypoint_2d_repair_response_valid.xlsx")
        else:
            pass

        self.print_static_info()

    def sign_keypoint_2d_result(self, json_path):
        try:
            json_content = read_json(json_path)
            # print("===> storageURL: ", json_content["storageURL"].split(os.sep))
            group_dict = dict()
            for group in json_content["LabelDetail"]["groupObject"]:
                group_dict[group["itemList"][0]] = group["itemList"][1:]
            objects_dict = dict()
            for objects in json_content["LabelDetail"]["objects"]:
                objects_dict[objects["featureId"]] = objects
            
            result = {
                "schema_version": "1.0",    # 写死
                "collect_metadata": {
                    "vin": json_content["storageURL"].split("/")[-4],
                    "collected_time": 1679573801,
                },
                "resources": [[{
                    "sensor": "FrontCam02",    # 写死
                    "uri": json_content["originStorageName"]
                },
                ]],
                "labeled_data": {
                    "metadata": {
                        "label_project": self.label_project,
                        "label_rule_version": self.label_rule_version,
                        "supplier": "coredata",
                        "delivery_date": str(datetime.now().date())
                    },
                    "frames": [
                        {
                            "properties": {"valid": "1"},
                            "groups": list()
                        }
                    ]
                }
            }
            groups = result["labeled_data"]["frames"][0]["groups"]
            self.number_of_objects += len(group_dict)
            for group, points in group_dict.items():
                shape_g, occluded_g, truncated_g, fuzzy_g, embedding_g = None, None, None, None, None
                for attr in objects_dict[group]["feature"]["attrs"]:
                    if attr["codeName"].strip() == "shape":
                        shape_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "occluded":
                        occluded_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "truncated":
                        truncated_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "fuzzy":
                        fuzzy_g = attr["attrs"][0]["codeName"].strip()
                    if attr["codeName"].strip() == "embedding":
                        embedding_g = attr["attrs"][0]["codeName"].strip()

                signpoint_object = {
                    "sensor": "FrontCam02",
                    "type": "bounding_box", # 写死
                    "geometry": "box_2d",   # 写死
                    "properties": {
                        "shape": shape_g,
                        "occluded": occluded_g,
                        "truncated": truncated_g,
                        "fuzzy": fuzzy_g,
                        "embedding": embedding_g

                    },
                    "points": [{
                        "x": float(objects_dict[group]["feature"]["xtl"]),
                        "y": float(objects_dict[group]["feature"]["ytl"]),
                    },{
                        "x": float(objects_dict[group]["feature"]["xbr"]),
                        "y": float(objects_dict[group]["feature"]["ybr"]),
                    }]
                }
                if objects_dict[group]["labelCode"] != "traffic_sign":
                    signpoint_object["properties"].pop("embedding")
                
                signpoint_objects = list()
                signpoint_objects.append(signpoint_object)
                
                for point in points:
                    # print(f'===> objects_dict: {objects_dict[point]}')
                    visible_p = None
                    for attr in objects_dict[point]["feature"]["attrs"]:
                        if attr["codeName"].strip() == "visible":
                            visible_p = attr["attrs"][0]["codeName"].strip()

                    signpoint_points = [
                        {
                            "x": float(objects_dict[point]["feature"]["points"][0]["x"]),
                            "y": float(objects_dict[point]["feature"]["points"][0]["y"]),
                            "properties": {
                                "visible": visible_p
                            }
                        }
                    ]
                    signpoint_objects.append({
                        "sensor": "FrontCam02",
                        "type": "key_point", # 写死
                        "geometry": "point",   # 写死
                        "points": signpoint_points
                    })

                groups.append(
                    {
                        "type": objects_dict[group]["labelCode"],
                        "objects": signpoint_objects
                    }
                )

            if len(groups) == 0:
                result["labeled_data"]["frames"][0]["properties"]["valid"] = "0"
            return result
            
        except Exception as err:
            print("===> json_content error: ", json_path)
            print("===> error info: ", err)
            data = f'{json_content["originStorageName"]}----{err}\n'
            write_txt(self.error_txt_path, data)
            return None

## ----------------------------------------------- sign keypoint 2D end -------------------------------------------------


## ----------------------------------------------- example -----------------------------------------------
if __name__ == "__main__":
    headers = {
        'Accept': 'application/json',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        # 'Origin': 'http://81.69.152.152',
        # 'Proxy-Connection': 'keep-alive',
        # 'Referer': 'http://81.69.152.152/api/doc/index.html',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
        'X-External-Name': 'coredata',
        'X-External-Token': '$@#vRQE45r141445i7k<UT<O98o98yytJ*%BAf',
    }

    # -------------------------------------- 正常数据还标配置 --------------------------------------
    url_return = 'http://10.20.0.22:31100/api/data/delivery/no_data_id'
    # ---------------------------------------------------------------------------------------------

    # -------------------------------------- 返修数据还标配置 --------------------------------------
    repair_batch = ""
    url_repair = 'http://10.20.0.22:31100/api/data/repair/delivery/no_data_id'
    # ---------------------------------------------------------------------------------------------

    # -------------------------------------- 数据配置 --------------------------------------
    """
    说明：假如需要还标一批数据（多次）， filter_data 保存了所有的已还标数据备份，每还标一次就修改一下 output_dir
    """
    input_dir = f"/home/kelaboss/coredata/returnlabels/data/核数聚-1215parkingslot-17_34_27-359-沈永琪"
    filter_data = f"/home/kelaboss/coredata/returnlabels/data/output"
    output_dir = f"{filter_data}\\/12.30"
    # -------------------------------------------------------------------------------------


    # -------------------------------------- park slot 还标 --------------------------------------
    label_project = "w3_ap_parking_slot_ipm"
    label_rule_version = "4.2"
    
    return_label = ParkingSlotReturnLabels(
        headers=headers, 
        url=url_return,                         # 正常还标用 url_return 返修还标用 url_repair
        label_project=label_project,            # 项目名称
        label_rule_version=label_rule_version,  # 项目版本
        input_dir=input_dir,                    # 待还标数据路径
        output_dir=output_dir,                  # 输出路径 保存上传成功数据备份和失败的数据 在 excel 表格内
        debug=True,                            # 调试
        filter_data=filter_data,                # 已经上传成功数据路径（需过滤掉）
        repair_batch=repair_batch,              # 返修还标的 repair_batch 名称
    ) 
    return_label.parking_slot_return()
    # --------------------------------------------------------------------------------------------
